/* Includes ------------------------------------------------------------------*/
#include "gd32f3x0.h"
#include "gd32f3x0_hw.h"
#include "Sys_Def.h"
#include "Log.h"
#include "Msg.h"
#include "SPI_Flash.h"
#include "UART_Ctrl.h"
#include "Exti_Ctrl.h"
#include "Systick_Ctrl.h"
#include "UART_Upgrade.h"
#include "VCOM_Upgrade.h"
#include "UART_Com.h"
#include "drv_usb_hw.h"
#include "cdc_acm_core.h"

#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)

uint32_t g_SystickIRQs = 0, g_OldSystickIRQs = 0;
/* Private function  ---------------------------------------------------------*/
/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
static void RCU_Config(void)
{
    /* Enable Peripheral clocks */
    rcu_periph_clock_enable(RCU_DMA);
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_USART0);
    rcu_periph_clock_enable(RCU_USART1);
#ifdef EN_I2C
    rcu_periph_clock_enable(RCU_I2C0);
#endif
#ifdef EN_SPI0
    rcu_periph_clock_enable(RCU_SPI0);
#endif
}

static void GPIO_Config(void)
{
    /* USART0 Pins configuration **************************************************/
#ifdef PA_USART0
    /* connect port to USART0_TX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
    /* connect port to USART0_RX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
#else
    /* connect port to USART0_TX */
    gpio_af_set(GPIOB, GPIO_AF_0, GPIO_PIN_6);
    /* connect port to USART0_RX */
    gpio_af_set(GPIOB, GPIO_AF_0, GPIO_PIN_7);
    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_6);
    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_7);
#endif

    /* USART1 Pins configuration **************************************************/
    /* connect port to USART1_TX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_2);
    /* connect port to USART1_RX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_3);
    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_3);

#ifdef EN_I2C
    /* I2C0 GPIO ports ---------------------------------*/
    /* connect PB6 to I2C0_SCL */
    gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_6);
    /* connect PB7 to I2C0_SDA */
    gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_7);
    /* configure GPIO pins of I2C0 */
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
#endif

#ifdef EN_SPI0
    /* SPI0 GPIO ports --------------------------------*/
    /* configure SPI0 GPIO: SCK/PA5, MOSI/PA7 */
    gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_5 | GPIO_PIN_7);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
#endif
}

int main(void)
{
#ifdef __BOOT_LOADER__
    nvic_vector_table_set(FMC_START_ADDR, 0x0);
#else
    uint32_t MsgParam;
    int MsgId = 0;
    nvic_vector_table_set(USER_APP_ADDR, 0x0);
#endif
    usb_rcu_disable();
    usb_intr_disable();
    usb_timer_deinit();
    __enable_irq();

    RCU_Config();
    GPIO_Config();
    UART_Config();
    Exti_Config();
    Systick_Config();

    usb_rcu_config();
    usb_timer_init();
    usbd_init(&cdc_acm, USB_CORE_ENUM_FS, &cdc_desc, &cdc_class);
    usb_intr_config();

#ifdef __BOOT_LOADER__
    if((*(__IO uint32_t*)USER_DATA_ADDR) == ONLINE_UART_UPGRADE_FLAG) {
        printf("Start UART ON_LINE Upgrading...\r\n");
        fmc_unlock();
        fmc_page_erase(USER_DATA_ADDR);
        fmc_lock();
        do {
            UART_Upgrade_Check(UPGRADE_UART);
        }while(1);
    } else if ((*(__IO uint32_t*)USER_DATA_ADDR) == ONLINE_VCOM_UPGRADE_FLAG) {
        printf("Start VCOM ON_LINE Upgrading...\r\n");
        fmc_unlock();
        fmc_page_erase(USER_DATA_ADDR);
        fmc_lock();
        do {
            VCOM_Upgrade_Check();
        } while(1);
    }
#endif

    printf("\r\n\r\n");
    LOG_PRINT(("Build Time:%s %s\r\n", __DATE__, __TIME__));
#ifdef __BOOT_LOADER__
    LOG_PRINT(("Bootloader Booting...\r\n"));
__check__:
    UART_Upgrade_Check(UPGRADE_UART);
    VCOM_Upgrade_Check();
    iap_load_app(USER_APP_ADDR);
    Systick_Delay(10);
    goto __check__;
#else
    LOG_PRINT(("Application Running...\r\n"));
    DIB542_MsgInit();
    UART_ComStart();

    while(1)
    {
        if(g_SystickIRQs - g_OldSystickIRQs == 5)
        {
            g_OldSystickIRQs = g_SystickIRQs;
            LOG_PRINT(("System Ticks: %d second\r\n", g_SystickIRQs));
        }

        Systick_Delay(10);

        while(DIB542_MsgGet(&MsgId, &MsgParam) == 0)
        {
        }

        UART_ComProc();

        if(USBD_CONFIGURED == cdc_acm.dev.cur_status) {
            if(0U == cdc_acm_check_ready(&cdc_acm)) {
                cdc_acm_data_receive(&cdc_acm, 0, 0);
            } else {
                cdc_acm_data_loop_send(&cdc_acm);
            }
        }
    }
#endif
}

/**
  * @brief  Retarget the C library printf function to the USART.
  * @param  None
  * @retval None
  */
int fputc(int ch, FILE *f)
{
    /* Loop until transmit data register is empty */
    while(usart_flag_get(DEBUG_UART, USART_FLAG_TBE) == RESET)
    {
    }
    /* Place your implementation of fputc here */
    usart_data_transmit(DEBUG_UART, (uint8_t)ch);
    return ch;
}
